പുതിയ ജാവാസ്ക്രിപ്റ്റ് ഇറ്ററേറ്റർ `scan` ഹെൽപ്പറിന്റെ ശക്തി കണ്ടെത്തുക. `reduce`-നപ്പുറം സ്ട്രീം പ്രോസസ്സിംഗ്, സ്റ്റേറ്റ് മാനേജ്മെന്റ്, ഡാറ്റാ അഗ്രഗേഷൻ എന്നിവയിൽ ഇത് എങ്ങനെ വിപ്ലവം സൃഷ്ടിക്കുന്നുവെന്ന് അറിയുക.
ജാവാസ്ക്രിപ്റ്റ് ഇറ്ററേറ്റർ `scan`: അക്യുമുലേറ്റീവ് സ്ട്രീം പ്രോസസ്സിംഗിന്റെ വിട്ടുപോയ കണ്ണി
ആധുനിക വെബ് ഡെവലപ്മെന്റിന്റെ നിരന്തരം വികസിച്ചുകൊണ്ടിരിക്കുന്ന ലോകത്ത്, ഡാറ്റയാണ് രാജാവ്. ഉപയോക്തൃ ഇവന്റുകൾ, തത്സമയ API പ്രതികരണങ്ങൾ, വലിയ ഡാറ്റാസെറ്റുകൾ തുടങ്ങി വിവരങ്ങളുടെ ഒരു പ്രവാഹമാണ് നമ്മൾ നിരന്തരം കൈകാര്യം ചെയ്യുന്നത്. ഈ ഡാറ്റ കാര്യക്ഷമമായും വ്യക്തമായും പ്രോസസ്സ് ചെയ്യുക എന്നത് ഒരു പ്രധാന വെല്ലുവിളിയാണ്. വർഷങ്ങളായി, ജാവാസ്ക്രിപ്റ്റ് ഡെവലപ്പർമാർ ഒരു അറേയെ ഒരൊറ്റ മൂല്യത്തിലേക്ക് ചുരുക്കാൻ ശക്തമായ Array.prototype.reduce മെത്തേഡിനെ ആശ്രയിച്ചിരുന്നു. എന്നാൽ നിങ്ങൾക്ക് ലക്ഷ്യസ്ഥാനം മാത്രമല്ല, യാത്രയും കാണണമെങ്കിൽ എന്തുചെയ്യും? ഒരു സങ്കലനത്തിന്റെ ഓരോ ഇടക്കാല ഘട്ടവും നിരീക്ഷിക്കണമെങ്കിൽ എന്തുചെയ്യും?
ഇവിടെയാണ് ഒരു പുതിയ, ശക്തമായ ഉപകരണം രംഗപ്രവേശം ചെയ്യുന്നത്: ഇറ്ററേറ്റർ scan ഹെൽപ്പർ. TC39 ഇറ്ററേറ്റർ ഹെൽപ്പേഴ്സ് പ്രൊപ്പോസലിന്റെ ഭാഗമായി, നിലവിൽ സ്റ്റേജ് 3-ൽ ഉള്ള scan, ജാവാസ്ക്രിപ്റ്റിൽ നമ്മൾ സീക്വൻഷ്യൽ, സ്ട്രീം അധിഷ്ഠിത ഡാറ്റ കൈകാര്യം ചെയ്യുന്ന രീതിയിൽ വിപ്ലവം സൃഷ്ടിക്കാൻ ഒരുങ്ങുകയാണ്. ഇത് reduce-ന്റെ ഫങ്ഷണലും മനോഹരവുമായ ഒരു പ്രതിരൂപമാണ്, അത് ഒരു ഓപ്പറേഷന്റെ പൂർണ്ണമായ ചരിത്രം നൽകുന്നു.
ഈ സമഗ്രമായ ഗൈഡ് നിങ്ങളെ scan മെത്തേഡിന്റെ ആഴങ്ങളിലേക്ക് കൊണ്ടുപോകും. ഇത് പരിഹരിക്കുന്ന പ്രശ്നങ്ങൾ, അതിന്റെ സിന്റാക്സ്, ലളിതമായ റണ്ണിംഗ് ടോട്ടലുകൾ മുതൽ സങ്കീർണ്ണമായ സ്റ്റേറ്റ് മാനേജ്മെന്റ് വരെയുള്ള അതിന്റെ ശക്തമായ ഉപയോഗങ്ങൾ, ആധുനികവും മെമ്മറി-കാര്യക്ഷമവുമായ ജാവാസ്ക്രിപ്റ്റിന്റെ വിശാലമായ ലോകത്ത് ഇത് എങ്ങനെ യോജിക്കുന്നു എന്നും നമ്മൾ പര്യവേക്ഷണം ചെയ്യും.
പരിചിതമായ വെല്ലുവിളി: `reduce`-ന്റെ പരിമിതികൾ
scan ടേബിളിലേക്ക് എന്ത് കൊണ്ടുവരുന്നു എന്ന് ശരിക്കും മനസ്സിലാക്കാൻ, നമുക്ക് ആദ്യം ഒരു സാധാരണ സാഹചര്യം വീണ്ടും പരിശോധിക്കാം. നിങ്ങൾക്ക് സാമ്പത്തിക ഇടപാടുകളുടെ ഒരു സ്ട്രീം ഉണ്ടെന്ന് കരുതുക, ഓരോ ഇടപാടിനും ശേഷമുള്ള റണ്ണിംഗ് ബാലൻസ് നിങ്ങൾ കണക്കാക്കേണ്ടതുണ്ട്. ഡാറ്റ ഇങ്ങനെയായിരിക്കാം:
const transactions = [100, -20, 50, -10, 75]; // Deposits and withdrawals
നിങ്ങൾക്ക് അവസാനത്തെ ബാലൻസ് മാത്രം മതിയായിരുന്നെങ്കിൽ, Array.prototype.reduce അതിന് ഏറ്റവും അനുയോജ്യമായ ഉപകരണമാണ്:
const finalBalance = transactions.reduce((balance, transaction) => balance + transaction, 0);
console.log(finalBalance); // Output: 195
ഇത് സംക്ഷിപ്തവും ഫലപ്രദവുമാണ്. എന്നാൽ നിങ്ങൾക്ക് കാലക്രമേണ അക്കൗണ്ട് ബാലൻസ് ഒരു ചാർട്ടിൽ വരയ്ക്കണമെങ്കിൽ എന്തുചെയ്യും? നിങ്ങൾക്ക് ഓരോ ഇടപാടിനും ശേഷമുള്ള ബാലൻസ് ആവശ്യമാണ്: [100, 80, 130, 120, 195]. reduce മെത്തേഡ് ഈ ഇടക്കാല ഘട്ടങ്ങളെ നമ്മിൽ നിന്ന് മറയ്ക്കുന്നു; അത് അവസാന ഫലം മാത്രമേ നൽകുന്നുള്ളൂ.
അപ്പോൾ, നമ്മൾ ഇത് പരമ്പരാഗതമായി എങ്ങനെ പരിഹരിക്കും? നമ്മൾ ഒരുപക്ഷേ ഒരു എക്സ്റ്റേണൽ സ്റ്റേറ്റ് വേരിയബിൾ ഉപയോഗിച്ച് ഒരു മാനുവൽ ലൂപ്പിലേക്ക് മടങ്ങിപ്പോകും:
const transactions = [100, -20, 50, -10, 75];
const runningBalances = [];
let currentBalance = 0;
for (const transaction of transactions) {
currentBalance += transaction;
runningBalances.push(currentBalance);
}
console.log(runningBalances); // Output: [100, 80, 130, 120, 195]
ഇത് പ്രവർത്തിക്കുന്നു, പക്ഷേ ഇതിന് നിരവധി പോരായ്മകളുണ്ട്:
- ഇംപറേറ്റീവ് സ്റ്റൈൽ: ഇത് അത്ര ഡിക്ലറേറ്റീവ് അല്ല. നമ്മൾ സ്റ്റേറ്റ് (
currentBalance), ഫലങ്ങളുടെ ശേഖരം (runningBalances) എന്നിവ സ്വമേധയാ കൈകാര്യം ചെയ്യുന്നു. - സ്റ്റേറ്റ്ഫുൾ ആൻഡ് വെർബോസ്: ലൂപ്പിന് പുറത്ത് മാറ്റം വരുത്താവുന്ന വേരിയബിളുകൾ കൈകാര്യം ചെയ്യേണ്ടതുണ്ട്, ഇത് കൂടുതൽ സങ്കീർണ്ണമായ സാഹചര്യങ്ങളിൽ കോഗ്നിറ്റീവ് ലോഡ് വർദ്ധിപ്പിക്കുകയും ബഗുകൾക്ക് സാധ്യതയുണ്ടാക്കുകയും ചെയ്യും.
- നോട്ട് കമ്പോസിബിൾ: ഇത് ഒരു വൃത്തിയുള്ള, ചെയിൻ ചെയ്യാവുന്ന ഓപ്പറേഷൻ അല്ല. ഇത് ഫങ്ഷണൽ മെത്തേഡ് ചെയിനിംഗിന്റെ (
map,filterപോലുള്ളവ) ഒഴുക്കിനെ തകർക്കുന്നു.
ഇറ്ററേറ്റർ scan ഹെൽപ്പർ കൃത്യമായി ഈ പ്രശ്നം ചാരുതയോടും ശക്തിയോടും കൂടി പരിഹരിക്കാനാണ് രൂപകൽപ്പന ചെയ്തിരിക്കുന്നത്.
ഒരു പുതിയ മാതൃക: ഇറ്ററേറ്റർ ഹെൽപ്പേഴ്സ് പ്രൊപ്പോസൽ
നമ്മൾ നേരിട്ട് scan-ലേക്ക് കടക്കുന്നതിന് മുൻപ്, അത് നിലനിൽക്കുന്ന പശ്ചാത്തലം മനസ്സിലാക്കേണ്ടത് പ്രധാനമാണ്. ഇറ്ററേറ്റർ ഹെൽപ്പേഴ്സ് പ്രൊപ്പോസൽ, ഡാറ്റാ പ്രോസസ്സിംഗിനായി ജാവാസ്ക്രിപ്റ്റിൽ ഇറ്ററേറ്ററുകളെ ഫസ്റ്റ് ക്ലാസ് സിറ്റിസൺസ് ആക്കാൻ ലക്ഷ്യമിടുന്നു. ജാവാസ്ക്രിപ്റ്റിലെ ഒരു അടിസ്ഥാന ആശയമാണ് ഇറ്ററേറ്ററുകൾ—for...of ലൂപ്പുകൾ, സ്പ്രെഡ് സിന്റാക്സ് (...), ജനറേറ്ററുകൾ എന്നിവയുടെയെല്ലാം പിന്നിലെ എഞ്ചിൻ അവയാണ്.
ഈ പ്രൊപ്പോസൽ Iterator.prototype-ലേക്ക് നേരിട്ട് പരിചിതമായ, അറേ പോലുള്ള ഒരു കൂട്ടം മെത്തേഡുകൾ ചേർക്കുന്നു, അവയിൽ ഉൾപ്പെടുന്നവ:
map(mapperFn): ഇറ്ററേറ്ററിലെ ഓരോ ഐറ്റത്തെയും രൂപാന്തരപ്പെടുത്തുന്നു.filter(filterFn): ഒരു ടെസ്റ്റ് പാസാകുന്ന ഐറ്റങ്ങൾ മാത്രം നൽകുന്നു.take(limit): ആദ്യത്തെ N ഐറ്റങ്ങൾ നൽകുന്നു.drop(limit): ആദ്യത്തെ N ഐറ്റങ്ങൾ ഒഴിവാക്കുന്നു.flatMap(mapperFn): ഓരോ ഐറ്റത്തെയും ഒരു ഇറ്ററേറ്ററിലേക്ക് മാപ്പ് ചെയ്യുകയും ഫലം ഫ്ലാറ്റൻ ചെയ്യുകയും ചെയ്യുന്നു.reduce(reducer, initialValue): ഇറ്ററേറ്ററിനെ ഒരൊറ്റ മൂല്യത്തിലേക്ക് ചുരുക്കുന്നു.- പിന്നെ, തീർച്ചയായും,
scan(reducer, initialValue).
ഇവിടെ പ്രധാന നേട്ടം ലേസി ഇവാലുവേഷൻ ആണ്. അറേ മെത്തേഡുകളിൽ നിന്ന് വ്യത്യസ്തമായി, മെമ്മറിയിൽ പുതിയ, ഇടക്കാല അറേകൾ സൃഷ്ടിക്കുന്ന ഇറ്ററേറ്റർ ഹെൽപ്പറുകൾ, ആവശ്യാനുസരണം ഓരോ ഐറ്റമായി പ്രോസസ്സ് ചെയ്യുന്നു. ഇത് വളരെ വലുതോ അനന്തമോ ആയ ഡാറ്റാ സ്ട്രീമുകൾ കൈകാര്യം ചെയ്യുന്നതിന് അവയെ അവിശ്വസനീയമാംവിധം മെമ്മറി-കാര്യക്ഷമമാക്കുന്നു.
`scan` മെത്തേഡിന്റെ ആഴങ്ങളിലേക്ക്
scan മെത്തേഡ് ആശയപരമായി reduce-ന് സമാനമാണ്, പക്ഷേ ഒരൊറ്റ അന്തിമ മൂല്യം നൽകുന്നതിന് പകരം, അത് ഓരോ ഘട്ടത്തിലും റിഡ്യൂസർ ഫംഗ്ഷന്റെ ഫലം നൽകുന്ന ഒരു പുതിയ ഇറ്ററേറ്റർ നൽകുന്നു. സങ്കലനത്തിന്റെ പൂർണ്ണമായ ചരിത്രം കാണാൻ ഇത് നിങ്ങളെ അനുവദിക്കുന്നു.
സിന്റാക്സും പാരാമീറ്ററുകളും
മെത്തേഡ് സിഗ്നേച്ചർ ലളിതമാണ്, reduce ഉപയോഗിച്ചിട്ടുള്ള ആർക്കും ഇത് പരിചിതമായിരിക്കും.
iterator.scan(reducer [, initialValue])
reducer(accumulator, element, index): ഇറ്ററേറ്ററിലെ ഓരോ എലമെന്റിനും വേണ്ടി വിളിക്കപ്പെടുന്ന ഒരു ഫംഗ്ഷൻ. ഇതിന് ലഭിക്കുന്നത്:accumulator: റിഡ്യൂസറിന്റെ മുൻ ഇൻവോക്കേഷൻ നൽകിയ മൂല്യം, അല്ലെങ്കിൽinitialValueനൽകിയിട്ടുണ്ടെങ്കിൽ അത്.element: സോഴ്സ് ഇറ്ററേറ്ററിൽ നിന്ന് പ്രോസസ്സ് ചെയ്യുന്ന നിലവിലെ എലമെന്റ്.index: നിലവിലെ എലമെന്റിന്റെ ഇൻഡെക്സ്.
accumulatorആയി ഉപയോഗിക്കുന്നു, കൂടാതെscanയീൽഡ് ചെയ്യുന്ന മൂല്യവും അതാണ്.initialValue(ഓപ്ഷണൽ): ആദ്യത്തെaccumulatorആയി ഉപയോഗിക്കാനുള്ള ഒരു പ്രാരംഭ മൂല്യം. നൽകിയിട്ടില്ലെങ്കിൽ, ഇറ്ററേറ്ററിന്റെ ആദ്യത്തെ എലമെന്റ് പ്രാരംഭ മൂല്യമായി ഉപയോഗിക്കുകയും, ആവർത്തനം രണ്ടാമത്തെ എലമെന്റിൽ നിന്ന് ആരംഭിക്കുകയും ചെയ്യും.
ഇത് എങ്ങനെ പ്രവർത്തിക്കുന്നു: ഘട്ടം ഘട്ടമായി
scan പ്രവർത്തനത്തിൽ കാണാൻ നമുക്ക് നമ്മുടെ റണ്ണിംഗ് ബാലൻസ് ഉദാഹരണം പിന്തുടരാം. ഓർക്കുക, scan ഇറ്ററേറ്ററുകളിലാണ് പ്രവർത്തിക്കുന്നത്, അതിനാൽ ആദ്യം, നമ്മുടെ അറേയിൽ നിന്ന് ഒരു ഇറ്ററേറ്റർ ലഭിക്കണം.
const transactions = [100, -20, 50, -10, 75];
const initialBalance = 0;
// 1. Get an iterator from the array
const transactionIterator = transactions.values();
// 2. Apply the scan method
const runningBalanceIterator = transactionIterator.scan(
(balance, transaction) => balance + transaction,
initialBalance
);
// 3. The result is a new iterator. We can convert it to an array to see the results.
const runningBalances = [...runningBalanceIterator];
console.log(runningBalances); // Output: [100, 80, 130, 120, 195]
ഇവിടെ പശ്ചാത്തലത്തിൽ സംഭവിക്കുന്നത് ഇതാണ്:
scanഒരു റിഡ്യൂസർ(a, b) => a + b, ഒരുinitialValueആയ0എന്നിവ ഉപയോഗിച്ച് വിളിക്കുന്നു.- ആവർത്തനം 1: റിഡ്യൂസർ
accumulator = 0(പ്രാരംഭ മൂല്യം),element = 100എന്നിവയുമായി വിളിക്കപ്പെടുന്നു. ഇത്100നൽകുന്നു.scan,100യീൽഡ് ചെയ്യുന്നു. - ആവർത്തനം 2: റിഡ്യൂസർ
accumulator = 100(മുൻ ഫലം),element = -20എന്നിവയുമായി വിളിക്കപ്പെടുന്നു. ഇത്80നൽകുന്നു.scan,80യീൽഡ് ചെയ്യുന്നു. - ആവർത്തനം 3: റിഡ്യൂസർ
accumulator = 80,element = 50എന്നിവയുമായി വിളിക്കപ്പെടുന്നു. ഇത്130നൽകുന്നു.scan,130യീൽഡ് ചെയ്യുന്നു. - ആവർത്തനം 4: റിഡ്യൂസർ
accumulator = 130,element = -10എന്നിവയുമായി വിളിക്കപ്പെടുന്നു. ഇത്120നൽകുന്നു.scan,120യീൽഡ് ചെയ്യുന്നു. - ആവർത്തനം 5: റിഡ്യൂസർ
accumulator = 120,element = 75എന്നിവയുമായി വിളിക്കപ്പെടുന്നു. ഇത്195നൽകുന്നു.scan,195യീൽഡ് ചെയ്യുന്നു.
മാനുവൽ ലൂപ്പുകളോ എക്സ്റ്റേണൽ സ്റ്റേറ്റ് മാനേജ്മെന്റോ ഇല്ലാതെ, നമുക്ക് ആവശ്യമുള്ളത് കൃത്യമായി നേടാനുള്ള ഒരു വൃത്തിയുള്ള, ഡിക്ലറേറ്റീവായ, കമ്പോസിബിൾ ആയ മാർഗ്ഗമാണ് ഫലം.
പ്രായോഗിക ഉദാഹരണങ്ങളും ആഗോള ഉപയോഗങ്ങളും
scan-ന്റെ ശക്തി ലളിതമായ റണ്ണിംഗ് ടോട്ടലുകൾക്കപ്പുറം വ്യാപിക്കുന്നു. ഇത് സ്ട്രീം പ്രോസസ്സിംഗിനുള്ള ഒരു അടിസ്ഥാന പ്രിമിറ്റീവാണ്, അത് ലോകമെമ്പാടുമുള്ള ഡെവലപ്പർമാർക്ക് പ്രസക്തമായ വിവിധ മേഖലകളിൽ പ്രയോഗിക്കാൻ കഴിയും.
ഉദാഹരണം 1: സ്റ്റേറ്റ് മാനേജ്മെന്റും ഇവന്റ് സോഴ്സിംഗും
scan-ന്റെ ഏറ്റവും ശക്തമായ പ്രയോഗങ്ങളിലൊന്ന് സ്റ്റേറ്റ് മാനേജ്മെന്റിലാണ്, ഇത് Redux പോലുള്ള ലൈബ്രറികളിൽ കാണുന്ന പാറ്റേണുകൾക്ക് സമാനമാണ്. നിങ്ങൾക്ക് ഉപയോക്തൃ പ്രവർത്തനങ്ങളുടെയോ ആപ്ലിക്കേഷൻ ഇവന്റുകളുടെയോ ഒരു സ്ട്രീം ഉണ്ടെന്ന് സങ്കൽപ്പിക്കുക. ഈ ഇവന്റുകൾ പ്രോസസ്സ് ചെയ്യാനും ഓരോ സമയത്തും നിങ്ങളുടെ ആപ്ലിക്കേഷന്റെ സ്റ്റേറ്റ് നിർമ്മിക്കാനും നിങ്ങൾക്ക് scan ഉപയോഗിക്കാം.
നമുക്ക് ഇൻക്രിമെന്റ്, ഡിക്രിമെന്റ്, റീസെറ്റ് പ്രവർത്തനങ്ങളുള്ള ഒരു ലളിതമായ കൗണ്ടർ മോഡൽ ചെയ്യാം.
// A generator function to simulate a stream of actions
function* actionStream() {
yield { type: 'INCREMENT' };
yield { type: 'INCREMENT' };
yield { type: 'DECREMENT', payload: 2 };
yield { type: 'UNKNOWN_ACTION' }; // Should be ignored
yield { type: 'RESET' };
yield { type: 'INCREMENT', payload: 5 };
}
// The initial state of our application
const initialState = { count: 0 };
// The reducer function defines how state changes in response to actions
function stateReducer(state, action) {
switch (action.type) {
case 'INCREMENT':
return { ...state, count: state.count + (action.payload || 1) };
case 'DECREMENT':
return { ...state, count: state.count - (action.payload || 1) };
case 'RESET':
return { count: 0 };
default:
return state; // IMPORTANT: Always return the current state for unhandled actions
}
}
// Use scan to create an iterator of the application's state history
const stateHistoryIterator = actionStream().scan(stateReducer, initialState);
// Log each state change as it happens
for (const state of stateHistoryIterator) {
console.log(state);
}
/*
Output:
{ count: 1 }
{ count: 2 }
{ count: 0 }
{ count: 0 } // a.k.a state was unchanged by UNKNOWN_ACTION
{ count: 0 } // after RESET
{ count: 5 }
*/
ഇത് അവിശ്വസനീയമാംവിധം ശക്തമാണ്. നമ്മുടെ സ്റ്റേറ്റ് എങ്ങനെ വികസിക്കുന്നു എന്ന് നമ്മൾ ഡിക്ലറേറ്റീവായി നിർവചിക്കുകയും, ആ സ്റ്റേറ്റിന്റെ പൂർണ്ണവും നിരീക്ഷിക്കാവുന്നതുമായ ഒരു ചരിത്രം സൃഷ്ടിക്കാൻ scan ഉപയോഗിക്കുകയും ചെയ്തു. ഈ പാറ്റേൺ ടൈം-ട്രാവൽ ഡീബഗ്ഗിംഗ്, ലോഗിംഗ്, പ്രവചിക്കാവുന്ന ആപ്ലിക്കേഷനുകൾ നിർമ്മിക്കൽ എന്നിവയ്ക്ക് അടിസ്ഥാനമാണ്.
ഉദാഹരണം 2: വലിയ സ്ട്രീമുകളിലെ ഡാറ്റാ അഗ്രഗേഷൻ
നിങ്ങൾ ഒരു വലിയ ലോഗ് ഫയൽ അല്ലെങ്കിൽ മെമ്മറിയിൽ ഒതുങ്ങാത്തത്ര വലിയ IoT സെൻസറുകളിൽ നിന്നുള്ള ഡാറ്റാ സ്ട്രീം പ്രോസസ്സ് ചെയ്യുകയാണെന്ന് സങ്കൽപ്പിക്കുക. ഇറ്ററേറ്റർ ഹെൽപ്പറുകൾ ഇവിടെ തിളങ്ങുന്നു. ഒരു സംഖ്യകളുടെ സ്ട്രീമിൽ ഇതുവരെ കണ്ട ഏറ്റവും ഉയർന്ന മൂല്യം ട്രാക്ക് ചെയ്യാൻ നമുക്ക് scan ഉപയോഗിക്കാം.
// A generator to simulate a very large stream of sensor readings
function* getSensorReadings() {
yield 22.5;
yield 24.1;
yield 23.8;
yield 28.3; // New max
yield 27.9;
yield 30.1; // New max
// ... could yield millions more
}
const readingsIterator = getSensorReadings();
// Use scan to track the maximum reading over time
const maxReadingHistory = readingsIterator.scan((maxSoFar, currentReading) => {
return Math.max(maxSoFar, currentReading);
});
// We don't need to pass an initialValue here. `scan` will use the first
// element (22.5) as the initial max and start from the second element.
console.log([...maxReadingHistory]);
// Output: [ 24.1, 24.1, 28.3, 28.3, 30.1 ]
ക്ഷമിക്കണം, ഔട്ട്പുട്ട് ഒറ്റനോട്ടത്തിൽ അല്പം തെറ്റായി തോന്നാം. നമ്മൾ ഒരു പ്രാരംഭ മൂല്യം നൽകാത്തതിനാൽ, scan ആദ്യത്തെ ഐറ്റം (22.5) പ്രാരംഭ അക്യുമുലേറ്ററായി ഉപയോഗിക്കുകയും ആദ്യത്തെ റിഡക്ഷന്റെ ഫലത്തിൽ നിന്ന് യീൽഡ് ചെയ്യാൻ തുടങ്ങുകയും ചെയ്തു. പ്രാരംഭ മൂല്യം ഉൾപ്പെടെയുള്ള ചരിത്രം കാണാൻ, നമുക്കത് വ്യക്തമായി നൽകാം, ഉദാഹരണത്തിന് -Infinity ഉപയോഗിച്ച്.
const maxReadingHistoryWithInitial = getSensorReadings().scan(
(maxSoFar, currentReading) => Math.max(maxSoFar, currentReading),
-Infinity
);
console.log([...maxReadingHistoryWithInitial]);
// Output: [ 22.5, 24.1, 24.1, 28.3, 28.3, 30.1 ]
ഇത് ഇറ്ററേറ്ററുകളുടെ മെമ്മറി കാര്യക്ഷമത പ്രകടമാക്കുന്നു. ഒരേ സമയം ഒന്നിൽ കൂടുതൽ മൂല്യം മെമ്മറിയിൽ സൂക്ഷിക്കാതെ തന്നെ നമുക്ക് സൈദ്ധാന്തികമായി അനന്തമായ ഡാറ്റാ സ്ട്രീം പ്രോസസ്സ് ചെയ്യാനും ഓരോ ഘട്ടത്തിലും റണ്ണിംഗ് മാക്സിമം നേടാനും കഴിയും.
ഉദാഹരണം 3: സങ്കീർണ്ണമായ ലോജിക്കിനായി മറ്റ് ഹെൽപ്പറുകളുമായി ചെയിനിംഗ്
ഇറ്ററേറ്റർ ഹെൽപ്പേഴ്സ് പ്രൊപ്പോസലിന്റെ യഥാർത്ഥ ശക്തി നിങ്ങൾ മെത്തേഡുകൾ ഒരുമിച്ച് ചെയിൻ ചെയ്യാൻ തുടങ്ങുമ്പോൾ അൺലോക്ക് ചെയ്യപ്പെടുന്നു. നമുക്ക് കൂടുതൽ സങ്കീർണ്ണമായ ഒരു പൈപ്പ്ലൈൻ നിർമ്മിക്കാം. ഒരു ഇ-കൊമേഴ്സ് ഇവന്റുകളുടെ സ്ട്രീം സങ്കൽപ്പിക്കുക. വിഐപി ഉപഭോക്താക്കൾ നൽകിയ വിജയകരമായി പൂർത്തിയാക്കിയ ഓർഡറുകളിൽ നിന്ന് മാത്രം കാലക്രമേണയുള്ള മൊത്തം വരുമാനം കണക്കാക്കാൻ നമ്മൾ ആഗ്രഹിക്കുന്നു.
function* getECommerceEvents() {
yield { type: 'PAGE_VIEW', user: 'guest' };
yield { type: 'ORDER_PLACED', user: 'user123', amount: 50, isVip: false };
yield { type: 'ORDER_COMPLETED', user: 'user456', amount: 120, isVip: true };
yield { type: 'ORDER_FAILED', user: 'user789', amount: 200, isVip: true };
yield { type: 'ORDER_COMPLETED', user: 'user101', amount: 75, isVip: true };
yield { type: 'PAGE_VIEW', user: 'user456' };
yield { type: 'ORDER_COMPLETED', user: 'user123', amount: 30, isVip: false }; // Not VIP
yield { type: 'ORDER_COMPLETED', user: 'user999', amount: 250, isVip: true };
}
const revenueHistory = getECommerceEvents()
// 1. Filter for the right events
.filter(event => event.type === 'ORDER_COMPLETED' && event.isVip)
// 2. Map to just the order amount
.map(event => event.amount)
// 3. Scan to get the running total
.scan((total, amount) => total + amount, 0);
console.log([...revenueHistory]);
// Let's trace the data flow:
// - After filter: { amount: 120 }, { amount: 75 }, { amount: 250 }
// - After map: 120, 75, 250
// - After scan (yielded values):
// - 0 + 120 = 120
// - 120 + 75 = 195
// - 195 + 250 = 445
// Final Output: [ 120, 195, 445 ]
ഈ ഉദാഹരണം ഡിക്ലറേറ്റീവ് പ്രോഗ്രാമിംഗിന്റെ മനോഹരമായ ഒരു പ്രകടനമാണ്. കോഡ് ബിസിനസ്സ് ലോജിക്കിന്റെ ഒരു വിവരണം പോലെ വായിക്കുന്നു: പൂർത്തിയായ വിഐപി ഓർഡറുകൾക്കായി ഫിൽട്ടർ ചെയ്യുക, തുക വേർതിരിച്ചെടുക്കുക, തുടർന്ന് റണ്ണിംഗ് ടോട്ടൽ കണക്കാക്കുക. ഓരോ ഘട്ടവും വലുതും മെമ്മറി-കാര്യക്ഷമവുമായ ഒരു പൈപ്പ്ലൈനിന്റെ ചെറുതും പുനരുപയോഗിക്കാവുന്നതും പരിശോധിക്കാവുന്നതുമായ ഒരു ഭാഗമാണ്.
`scan()` vs. `reduce()`: ഒരു വ്യക്തമായ വ്യത്യാസം
ഈ രണ്ട് ശക്തമായ മെത്തേഡുകൾ തമ്മിലുള്ള വ്യത്യാസം ഉറപ്പിക്കേണ്ടത് നിർണായകമാണ്. അവ ഒരു റിഡ്യൂസർ ഫംഗ്ഷൻ പങ്കിടുന്നുണ്ടെങ്കിലും, അവയുടെ ഉദ്ദേശ്യവും ഔട്ട്പുട്ടും അടിസ്ഥാനപരമായി വ്യത്യസ്തമാണ്.
reduce()സംഗ്രഹിക്കുന്നതിനെക്കുറിച്ചാണ്. ഇത് ഒരു മുഴുവൻ സീക്വൻസിനെയും പ്രോസസ്സ് ചെയ്ത് ഒരൊറ്റ, അന്തിമ മൂല്യം നിർമ്മിക്കുന്നു. യാത്ര മറഞ്ഞിരിക്കുന്നു.scan()രൂപാന്തരീകരണത്തെയും നിരീക്ഷണത്തെയും കുറിച്ചാണ്. ഇത് ഒരു സീക്വൻസിനെ പ്രോസസ്സ് ചെയ്യുകയും അതേ നീളത്തിലുള്ള ഒരു പുതിയ സീക്വൻസ് നിർമ്മിക്കുകയും ചെയ്യുന്നു, ഓരോ ഘട്ടത്തിലും സമാഹരിച്ച സ്റ്റേറ്റ് കാണിക്കുന്നു. യാത്രയാണ് ഫലം.
ഇവിടെ ഒരു വശങ്ങളിലായുള്ള താരതമ്യം:
| ഫീച്ചർ | iterator.reduce(reducer, initial) |
iterator.scan(reducer, initial) |
|---|---|---|
| പ്രാഥമിക ലക്ഷ്യം | ഒരു സീക്വൻസിനെ ഒരൊറ്റ സംഗ്രഹ മൂല്യത്തിലേക്ക് ചുരുക്കുക. | ഒരു സീക്വൻസിന്റെ ഓരോ ഘട്ടത്തിലും സമാഹരിച്ച മൂല്യം നിരീക്ഷിക്കുക. |
| റിട്ടേൺ വാല്യൂ | അന്തിമമായി സമാഹരിച്ച ഫലത്തിന്റെ ഒരൊറ്റ മൂല്യം (അസിൻക്രണസ് ആണെങ്കിൽ പ്രോമിസ്). | ഓരോ ഇടക്കാല സമാഹരിച്ച ഫലവും നൽകുന്ന ഒരു പുതിയ ഇറ്ററേറ്റർ. |
| സാധാരണ സാമ്യം | ഒരു ബാങ്ക് അക്കൗണ്ടിന്റെ അവസാന ബാലൻസ് കണക്കാക്കുന്നു. | ഓരോ ഇടപാടിനും ശേഷമുള്ള ബാലൻസ് കാണിക്കുന്ന ഒരു ബാങ്ക് സ്റ്റേറ്റ്മെന്റ് ഉണ്ടാക്കുന്നു. |
| ഉപയോഗം | സംഖ്യകൾ കൂട്ടുക, ഏറ്റവും വലിയത് കണ്ടെത്തുക, സ്ട്രിംഗുകൾ യോജിപ്പിക്കുക. | റണ്ണിംഗ് ടോട്ടലുകൾ, സ്റ്റേറ്റ് മാനേജ്മെന്റ്, മൂവിംഗ് ആവറേജുകൾ കണക്കാക്കൽ, ചരിത്രപരമായ ഡാറ്റ നിരീക്ഷിക്കൽ. |
കോഡ് താരതമ്യം
const numbers = [1, 2, 3, 4].values(); // Get an iterator
// Reduce: The destination
const sum = numbers.reduce((acc, val) => acc + val, 0);
console.log(sum); // Output: 10
// You need a new iterator for the next operation
const numbers2 = [1, 2, 3, 4].values();
// Scan: The journey
const runningSum = numbers2.scan((acc, val) => acc + val, 0);
console.log([...runningSum]); // Output: [1, 3, 6, 10]
ഇന്ന് ഇറ്ററേറ്റർ ഹെൽപ്പറുകൾ എങ്ങനെ ഉപയോഗിക്കാം
ഈ എഴുത്തിന്റെ സമയത്ത്, ഇറ്ററേറ്റർ ഹെൽപ്പേഴ്സ് പ്രൊപ്പോസൽ TC39 പ്രോസസ്സിൽ സ്റ്റേജ് 3-ലാണ്. ഇതിനർത്ഥം ഇത് അന്തിമമാക്കുന്നതിനും ECMAScript സ്റ്റാൻഡേർഡിന്റെ ഭാവി പതിപ്പിൽ ഉൾപ്പെടുത്തുന്നതിനും വളരെ അടുത്താണ്. എല്ലാ ബ്രൗസറുകളിലോ Node.js എൻവയോൺമെന്റുകളിലോ ഇത് നേറ്റീവായി ഇതുവരെ ലഭ്യമായേക്കില്ലെങ്കിലും, ഇത് ഉപയോഗിക്കാൻ നിങ്ങൾ കാത്തിരിക്കേണ്ടതില്ല.
പോളിഫില്ലുകളിലൂടെ നിങ്ങൾക്ക് ഈ ശക്തമായ ഫീച്ചറുകൾ ഇന്നുതന്നെ ഉപയോഗിക്കാം. ഏറ്റവും സാധാരണമായ മാർഗ്ഗം core-js ലൈബ്രറി ഉപയോഗിക്കുക എന്നതാണ്, ഇത് ആധുനിക ജാവാസ്ക്രിപ്റ്റ് ഫീച്ചറുകൾക്കുള്ള ഒരു സമഗ്രമായ പോളിഫിൽ ആണ്.
ഇത് ഉപയോഗിക്കുന്നതിന്, നിങ്ങൾ സാധാരണയായി core-js ഇൻസ്റ്റാൾ ചെയ്യും:
npm install core-js
തുടർന്ന് നിങ്ങളുടെ ആപ്ലിക്കേഷന്റെ എൻട്രി പോയിന്റിൽ നിർദ്ദിഷ്ട പ്രൊപ്പോസൽ പോളിഫിൽ ഇമ്പോർട്ട് ചെയ്യുക:
import 'core-js/proposals/iterator-helpers';
// Now you can use .scan() and other helpers!
const result = [1, 2, 3].values()
.map(x => x * 2)
.scan((a, b) => a + b, 0);
console.log([...result]); // [2, 6, 12]
അല്ലെങ്കിൽ, നിങ്ങൾ Babel പോലുള്ള ഒരു ട്രാൻസ്പൈലർ ഉപയോഗിക്കുകയാണെങ്കിൽ, സ്റ്റേജ് 3 പ്രൊപ്പോസലുകൾക്ക് ആവശ്യമായ പോളിഫില്ലുകളും ട്രാൻസ്ഫോമുകളും ഉൾപ്പെടുത്താൻ നിങ്ങൾക്ക് അത് കോൺഫിഗർ ചെയ്യാൻ കഴിയും.
ഉപസംഹാരം: ഡാറ്റയുടെ ഒരു പുതിയ യുഗത്തിനായുള്ള ഒരു പുതിയ ഉപകരണം
ജാവാസ്ക്രിപ്റ്റ് ഇറ്ററേറ്റർ scan ഹെൽപ്പർ ഒരു സൗകര്യപ്രദമായ പുതിയ മെത്തേഡ് എന്നതിലുപരി; ഇത് ഡാറ്റാ സ്ട്രീമുകൾ കൈകാര്യം ചെയ്യുന്നതിനുള്ള കൂടുതൽ ഫങ്ഷണൽ, ഡിക്ലറേറ്റീവ്, മെമ്മറി-കാര്യക്ഷമമായ ഒരു രീതിയിലേക്കുള്ള മാറ്റത്തെ പ്രതിനിധീകരിക്കുന്നു. ഇത് reduce അവശേഷിപ്പിച്ച ഒരു നിർണ്ണായക വിടവ് നികത്തുന്നു, ഡെവലപ്പർമാരെ ഒരു അന്തിമ ഫലത്തിൽ എത്താൻ മാത്രമല്ല, ഒരു സങ്കലനത്തിന്റെ മുഴുവൻ ചരിത്രവും നിരീക്ഷിക്കാനും അതിനനുസരിച്ച് പ്രവർത്തിക്കാനും അനുവദിക്കുന്നു.
scan-ഉം വിശാലമായ ഇറ്ററേറ്റർ ഹെൽപ്പേഴ്സ് പ്രൊപ്പോസലും സ്വീകരിക്കുന്നതിലൂടെ, നിങ്ങൾക്ക് എഴുതാൻ കഴിയുന്ന കോഡ് ഇതായിരിക്കും:
- കൂടുതൽ ഡിക്ലറേറ്റീവ്: മാനുവൽ ലൂപ്പുകൾ ഉപയോഗിച്ച് നിങ്ങൾ എങ്ങനെ നേടുന്നു എന്നതിലുപരി, നിങ്ങൾ എന്താണ് നേടാൻ ശ്രമിക്കുന്നതെന്ന് നിങ്ങളുടെ കോഡ് കൂടുതൽ വ്യക്തമായി പ്രകടിപ്പിക്കും.
- കൂടുതൽ കമ്പോസിബിൾ: വായിക്കാനും ന്യായീകരിക്കാനും എളുപ്പമുള്ള സങ്കീർണ്ണമായ ഡാറ്റാ പ്രോസസ്സിംഗ് പൈപ്പ്ലൈനുകൾ നിർമ്മിക്കുന്നതിന് ലളിതവും ശുദ്ധവുമായ പ്രവർത്തനങ്ങൾ ഒരുമിച്ച് ചെയിൻ ചെയ്യുക.
- കൂടുതൽ മെമ്മറി-കാര്യക്ഷമം: നിങ്ങളുടെ സിസ്റ്റത്തിന്റെ മെമ്മറിയെ അമിതമായി ഭാരപ്പെടുത്താതെ വലിയതോ അനന്തമോ ആയ ഡാറ്റാസെറ്റുകൾ പ്രോസസ്സ് ചെയ്യുന്നതിന് ലേസി ഇവാലുവേഷൻ പ്രയോജനപ്പെടുത്തുക.
നമ്മൾ കൂടുതൽ ഡാറ്റാ-ഇന്റൻസീവും റിയാക്ടീവുമായ ആപ്ലിക്കേഷനുകൾ നിർമ്മിക്കുന്നത് തുടരുമ്പോൾ, scan പോലുള്ള ഉപകരണങ്ങൾ ഒഴിച്ചുകൂടാനാവാത്തതായി മാറും. ഇവന്റ് സോഴ്സിംഗ്, സ്ട്രീം പ്രോസസ്സിംഗ് പോലുള്ള സങ്കീർണ്ണമായ പാറ്റേണുകൾ നേറ്റീവായും മനോഹരമായും കാര്യക്ഷമമായും നടപ്പിലാക്കാൻ സഹായിക്കുന്ന ഒരു ശക്തമായ പ്രിമിറ്റീവാണിത്. ഇന്നുതന്നെ ഇത് പര്യവേക്ഷണം ചെയ്യാൻ ആരംഭിക്കുക, ജാവാസ്ക്രിപ്റ്റിലെ ഡാറ്റാ കൈകാര്യം ചെയ്യലിന്റെ ഭാവിക്കായി നിങ്ങൾ നന്നായി തയ്യാറെടുക്കും.